/**
* @Author: zou.yingbin
* @Create  : 2020/9/4 14:59
* @Modify  : 2020/9/4 14:59
 */

package main

import (
	"fmt"
	"io"
	"log"
	"net"
	"strconv"
	"sync"
	"time"

	"crypto/sha256"
	"encoding/hex"

	miProto "MTPClientGo/mtp_proto"
	"github.com/satori/go.uuid"
	"google.golang.org/protobuf/proto"
)

type stGoods struct {
	goodsName string
	goodsCode string
	goodsId   uint32
	marketId  uint32
	decimal   uint32
}

type stPosition struct {
	GoodsCode string
	BuyOrSell string
	CurQty    string
	FrozenQty string
}

type stOrder struct {
	OrderTime   string
	OrderId     string
	GoodsCode   string
	OrderPrice  string
	BuyOrSell   string
	BuildType   string
	OrderQty    string
	OrderStatus string
	MarketId    string
	GoodsId     string
}

// 连接信息
type Session struct {
	conn       net.Conn
	strAcc     string
	strPwd     string
	bConnected bool
	bLogin     bool
	accountId  uint64
	loginId    uint64
	userId     uint32
	token      string
	quote      *QuoteSession
	lck        sync.RWMutex
	handleRsp  map[uint32]func(fid uint32, msg []byte)
	goodsMap   map[string]stGoods
	position   []stPosition
	bRecvPos   bool
	orders     []stOrder
	bRecvOrder bool
	orderLog   stOrderLog
}

func (r *Session) GetUUID() string {
	return uuid.NewV4().String()
}

// 执行登录等任务
func (r *Session) Do(strAcc, strPwd string) {
	var err error
	r.conn, err = net.Dial("tcp", Stcfg.BizAddr())
	if err != nil {
		fmt.Println(strAcc, "connect server fail")
		return
	}

	if Stcfg.PrintLog != 0 {
		fmt.Println(strAcc, " connect server success")
	}

	r.strAcc = strAcc
	r.strPwd = strPwd
	r.bConnected = true
	r.handleRsp = make(map[uint32]func(fid uint32, msg []byte))
	r.goodsMap = make(map[string]stGoods)
	r.position = make([]stPosition, 0)
	r.orderLog.Init()

	go r.doRecv()

	r.login()
}

func (r *Session) send(funCode, sessionId, serialNum uint32, msg []byte, bCrypto bool, label string) {
	if !r.bConnected {
		return
	}

	sendBuf, err := MakePacket(funCode, sessionId, serialNum, msg, bCrypto)
	if err != nil {
		fmt.Println("funCode:", funCode, "make login packet fail:", err)
		return
	}

	if valReqLog != nil && funCode != 0 {
		valReqLog.AddRequest(r.strAcc, label, serialNum)
	}

	_, err = r.conn.Write(sendBuf)
	if err != nil {
		fmt.Println("funCode:", funCode, "send packet fail:", err)
	}

	if Stcfg.PrintLog != 0 && funCode != 0 {
		log.Printf("%v send funcode[%d] sid[%d] serial[%d] len[%d] %s\n",
			r.strAcc, funCode, sessionId, serialNum, len(sendBuf), label)
	}
}

// 发送心跳包
func (r *Session) SendHeart() {
	if !r.bConnected {
		return
	}

	msg := make([]byte, 0)
	r.send(0, 0, 0, msg, false, "heart")
}

// 发送令牌校验
func (r *Session) SendTokenCheck() {
	if !r.bLogin || !r.bConnected {
		return
	}

	t := time.Now().Unix()

	req := miProto.TokenCheckReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_TokenCheckReq),
			RequestID:  proto.Uint32(1001),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		CheckType:  proto.Uint32(1),
		LoginID:    proto.Uint64(r.loginId),
		Token:      proto.String(r.token),
		SystemTime: proto.Uint64(uint64(t)),
	}

	msg, _ := proto.Marshal(&req)

	r.send(FID_TokenCheckReq, GetSerialID(), GetSerialID(), msg, true, "tokenCheckReq")
}

func (r *Session) IsLogin() bool {
	return r.bLogin
}

func (r *Session) login() {
	loginId, _ := strconv.ParseUint(r.strAcc, 10, 64)
	t := time.Now().Unix()
	d := &miProto.LoginReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_LoginReq),
			RequestID:  proto.Uint32(1001),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		LoginID:    proto.Uint64(loginId),
		LoginPWD:   proto.String(r.strPwd),
		LoginType:  proto.Uint32(0),
		ClientType: proto.Uint32(11),
		Version:    proto.String("1.0.0.0"),
		GUID:       proto.String(strconv.FormatInt(t, 10)),
		LoginIp:    proto.String(Stcfg.LocalIp),
		LoginPort:  proto.Uint32(0),
	}

	// 加密密码
	{
		str := r.strAcc + r.strPwd
		buf := sha256.Sum256([]byte(str))
		str2 := hex.EncodeToString(buf[0:])
		if Stcfg.LoginPwdCrypto == 0 {
			*d.LoginPWD = str2
		} else {
			*d.LoginPWD = r.strPwd
		}
	}

	msg, err := proto.Marshal(d)
	if err != nil {
		fmt.Println("Marshal msg fail:", err)
		return
	}

	// 发送登录报文
	r.send(FID_LoginReq, 1001, GetSerialID(), msg, true, "login")
}

func (r *Session) logout() {

	if !r.bLogin {
		return
	}

	loginId, _ := strconv.ParseUint(r.strAcc, 10, 64)
	t := time.Now().Unix()
	d := &miProto.LogoutReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_LogoutReq),
			RequestID:  proto.Uint32(1001),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		LoginID:   proto.Uint64(loginId),
		Token:     proto.String(r.token),
		LoginIp:   proto.String(Stcfg.LocalIp),
		LoginPort: proto.Uint32(0),
	}
	msg, _ := proto.Marshal(d)
	r.send(FID_LogoutReq, GetSerialID(), GetSerialID(), msg, true, "logout")
}

func (r *Session) qryExchange() {
	utc := time.Now().Unix()
	p := &miProto.ExternalExchangeReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(r.accountId),
			FunCode:    proto.Uint32(FID_ExternalExchangeReq),
			RequestID:  proto.Uint32(GetSerialID()),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(utc),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		ExUTime: proto.Uint64(uint64(utc)),
	}

	msg, _ := proto.Marshal(p)
	r.send(FID_ExternalExchangeReq, GetSerialID(), GetSerialID(), msg, true, "qryExchange")
}

func (r *Session) qryCommonReq(sqlId string, param map[string]string, label string) {
	if !r.bLogin {
		return
	}
	utc := time.Now().Unix()
	p := &miProto.QueryCommonReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(r.accountId),
			FunCode:    proto.Uint32(FID_QueryCommonReq),
			RequestID:  proto.Uint32(GetSerialID()),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(utc),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(r.userId),
		},
		Statement: proto.String(sqlId),
	}

	if param != nil && len(param) > 0 {
		p.ParamValues = make([]*miProto.ParamValue, 0)
		for k, v := range param {
			p.ParamValues = append(p.ParamValues, &miProto.ParamValue{Key: proto.String(k), Value: proto.String(v)})
		}
	}

	msg, _ := proto.Marshal(p)
	serialNum := GetSerialID()

	if label == "qryGoods" {
		r.lck.Lock()
		r.handleRsp[serialNum] = r.onQryGoodsRsp
		r.lck.Unlock()
	} else if label == "qryPosition" {
		r.lck.Lock()
		r.handleRsp[serialNum] = r.onQryPositionRsp
		r.lck.Unlock()
	} else if label == "qryOrder" {
		r.lck.Lock()
		r.handleRsp[serialNum] = r.onQryOrderRsp
		r.lck.Unlock()
	}

	r.send(FID_QueryCommonReq, GetSerialID(), serialNum, msg, true, label)
}

func (r *Session) qryGoods() {
	r.qryCommonReq("TIReqQryInstrument", nil, "qryGoods")
}

func (r *Session) onQryGoodsRsp(fid uint32, msg []byte) {
	fmt.Println("onQryGoodsRsp")
	var p = miProto.QueryCommonRsp{}
	if err := proto.Unmarshal(msg, &p); err != nil || fid != FID_QueryCommonRsp {
		return
	}

	if p.RetCode == nil || *p.RetCode != 0 {
		return
	}

	if p.Rsps == nil || len(p.Rsps) == 0 {
		return
	}

	var goodsName string
	var goodsCode string
	var goodsId uint32
	var marketId uint32
	var decimal uint32

	for _, rsp := range p.Rsps {
		for _, value := range rsp.RowValues {
			for i, rowValue := range value.RowValues {
				if *rsp.FieldInfos[i].FieldName == "INSTRUMENTID" {
					goodsCode = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "INSTRUMENTNAME" {
					goodsName = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "GOODSID" {
					if d, err := strconv.ParseInt(rowValue, 10, 32); err == nil {
						goodsId = uint32(d)
					}
				} else if *rsp.FieldInfos[i].FieldName == "MARKETID" {
					if d, err := strconv.ParseInt(rowValue, 10, 32); err == nil {
						marketId = uint32(d)
					}
				} else if *rsp.FieldInfos[i].FieldName == "PRICETICK" {
					if d, err := strconv.ParseInt(rowValue, 10, 32); err == nil {
						decimal = uint32(d)
					}
				}
			}

			r.goodsMap[goodsCode] = stGoods{
				goodsId:   goodsId,
				goodsName: goodsName,
				goodsCode: goodsCode,
				marketId:  marketId,
				decimal:   decimal,
			}

			//fmt.Println(goodsName, goodsId, goodsCode)
		}
	}
}

func (r *Session) qryFund() {
	//FID_LoginQueryReq
	if !r.bLogin {
		return
	}

	loginId, _ := strconv.ParseUint(r.strAcc, 10, 64)
	t := time.Now().Unix()
	d := &miProto.LoginQueryReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(0),
			FunCode:    proto.Uint32(FID_LoginQueryReq),
			RequestID:  proto.Uint32(1001),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(0),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(15201),
			UserID:     proto.Uint32(0),
		},
		LoginID:   proto.Uint64(loginId),
		QueryItem: proto.String("1,2"),
	}
	msg, _ := proto.Marshal(d)
	r.send(FID_LoginQueryReq, GetSerialID(), GetSerialID(), msg, true, "qryFund")
}

func (r *Session) qryPosition() {
	param := make(map[string]string)
	param["accountId"] = strconv.FormatUint(r.accountId, 10)
	r.lck.Lock()
	r.position = make([]stPosition, 0)
	r.bRecvPos = false
	r.lck.Unlock()
	r.qryCommonReq("SearchTradePositionDetail", param, "qryPosition")
}

func (r *Session) onQryPositionRsp(fid uint32, msg []byte) {
	//fmt.Println("onQryPositionRps")
	var p = miProto.QueryCommonRsp{}
	if err := proto.Unmarshal(msg, &p); err != nil || fid != FID_QueryCommonRsp {
		return
	}

	if p.RetCode == nil || *p.RetCode != 0 {
		return
	}

	if p.Rsps == nil || len(p.Rsps) == 0 {
		return
	}

	r.lck.Lock()

	var d stPosition

	for _, rsp := range p.Rsps {
		for _, value := range rsp.RowValues {
			for i, rowValue := range value.RowValues {
				if *rsp.FieldInfos[i].FieldName == "GOODSCODE" {
					d.GoodsCode = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "BUYORSELL" {
					d.BuyOrSell = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "CURPOSITIONQTY" {
					d.CurQty = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "FROZENQTY" {
					d.FrozenQty = rowValue
				}
			}

			r.position = append(r.position, d)
		}
	}

	fmt.Println(r.strAcc, "pos", r.position)
	r.bRecvPos = true
	r.lck.Unlock()
}

func (r *Session) qryOrder() {
	r.orders = make([]stOrder, 0)
	r.bRecvOrder = false
	param := make(map[string]string)
	param["accountId"] = strconv.FormatUint(r.accountId, 10)
	r.qryCommonReq("ClientInforInnerOrderDetail", param, "qryOrder")
}

func (r *Session) onQryOrderRsp(fid uint32, msg []byte) {
	var p = miProto.QueryCommonRsp{}
	if err := proto.Unmarshal(msg, &p); err != nil || fid != FID_QueryCommonRsp {
		return
	}

	if p.RetCode == nil || *p.RetCode != 0 {
		return
	}

	if p.Rsps == nil || len(p.Rsps) == 0 {
		return
	}

	r.lck.Lock()

	var d stOrder

	for _, rsp := range p.Rsps {
		for _, value := range rsp.RowValues {
			for i, rowValue := range value.RowValues {
				if *rsp.FieldInfos[i].FieldName == "GOODSCODE" {
					d.GoodsCode = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "BUYORSELL" {
					d.BuyOrSell = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "ORDERPRICE" {
					d.OrderPrice = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "OPENORDERQTY" {
					if len(rowValue) > 0 {
						if rowValue[0] != '0' {
							d.OrderQty = rowValue
						}
					}
				} else if *rsp.FieldInfos[i].FieldName == "CLOSEORDERQTY" {
					if len(rowValue) > 0 {
						if rowValue[0] != '0' {
							d.OrderQty = rowValue
						}
					}
				} else if *rsp.FieldInfos[i].FieldName == "ORDERTIME" {
					d.OrderTime = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "ORDERID" {
					d.OrderId = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "CHANNELINNERORDERSTATUS" {
					d.OrderStatus = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "CHANNELBUILDTYPE" {
					d.BuildType = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "GOODSID" {
					d.GoodsId = rowValue
				} else if *rsp.FieldInfos[i].FieldName == "MARKETID" {
					d.MarketId = rowValue
				}
			}

			r.orders = append(r.orders, d)
		}
	}

	r.bRecvOrder = true
	r.lck.Unlock()
}

func (r *Session) qryTrade() {
	param := make(map[string]string)
	param["accountId"] = strconv.FormatUint(r.accountId, 10)
	r.qryCommonReq("ClientInforInnerTradeDetail", param, "qryTrade")
}

func (r *Session) GetGoodsId(goodsCode string) uint32 {
	if g, ok := r.goodsMap[goodsCode]; ok {
		return g.goodsId
	}

	return 0
}

func (r *Session) SendOrder(marketId, goodsId, qty, buildType, buyOrSell uint32, orderPrice float64) {

	t := time.Now().Unix()

	req := miProto.ChannelOrderReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(r.accountId),
			FunCode:    proto.Uint32(FID_ChannelOrderReq),
			RequestID:  proto.Uint32(0),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(goodsId),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(marketId),
			UserID:     proto.Uint32(r.userId),
		},
		AccountID:          proto.Uint64(r.accountId),
		GoodsID:            proto.Uint32(goodsId),
		MarketID:           proto.Uint32(marketId),
		OrderQty:           proto.Uint64(uint64(qty)),
		OrderPrice:         proto.Float64(orderPrice),
		ClientSerialNo:     proto.String(strconv.FormatInt(t, 10)),
		ClientOrderTime:    proto.String(time.Now().Format("2006-01-02 15:04:05")),
		ClientType:         proto.Uint32(2),
		ChannelBuildType:   proto.Uint32(buildType),
		BuyOrSell:          proto.Uint32(buyOrSell),
		PriceMode:          proto.Uint32(1),
		HedgeFlag:          proto.Uint32(0),
		TimeValidType:      proto.Uint32(1),
		ChannelOperateType: proto.Uint32(1),
		ChannelOrderSrc:    proto.Uint32(1),
		ValidType:          proto.Int32(1),
		SPPoint:            proto.Uint32(0),
		SLPoint:            proto.Uint32(0),
		CloseType:          proto.Uint32(0),
		IP:                 proto.String(getLocalIP()),
	}

	if orderPrice > 0.001 {
		*req.PriceMode = 2
	}

	msg, _ := proto.Marshal(&req)
	serialNum := GetSerialID()
	r.orderLog.addReq(r.strAcc, *req.OrderQty, serialNum)
	r.send(FID_ChannelOrderReq, GetSerialID(), serialNum, msg, true, "channelOrderReq")
}

// 发送风控斩仓请求.
func (r *Session) SendRiskCloseReq(ui *stOrderUI) {
	goodsId := r.GetGoodsId(ui.GoodsCode)
	t := time.Now().Unix()
	req := miProto.RiskCloseReq{
		Header: &miProto.MessageHead{
			AccountID:  proto.Uint64(r.accountId),
			FunCode:    proto.Uint32(FID_RiskCloseReq),
			RequestID:  proto.Uint32(0),
			AccessID:   proto.Uint32(0),
			ClientTime: proto.Int64(t),
			GoodsID:    proto.Uint32(goodsId),
			UUID:       proto.String(r.GetUUID()),
			MarketID:   proto.Uint32(ui.MarketID),
			UserID:     proto.Uint32(r.userId),
		},
		AccountID:   proto.Uint64(r.accountId),
		GoodsID:     proto.Uint32(goodsId),
		MarketID:    proto.Uint32(ui.MarketID),
		OrderQty:    proto.Uint64(uint64(ui.Qty)),
		OrderPrice:  proto.Float64(ui.OrderPrice),
		BuyOrSell:   proto.Uint32(ui.BuyOrSell),
		PriceMode:   proto.Uint32(1),
		CloseType:   proto.Uint32(0),
		BuildType:   proto.Uint32(ui.BuildType),
		IP:          proto.String(getLocalIP()),
		OrderSrc:    proto.Uint32(2),
		OperateType: proto.Uint32(2),
	}

	if *req.OrderPrice > 0.001 {
		*req.PriceMode = 2
	}

	msg, _ := proto.Marshal(&req)
	serialNum := GetSerialID()
	r.send(FID_RiskCloseReq, GetSerialID(), serialNum, msg, true, "RiskCloseReq")
}

func (r *Session) SendCancelOrder(orderId uint64) {

	orderIdTxt := strconv.FormatUint(orderId, 10)
	var valO stOrder
	for _, order := range r.orders {
		if order.OrderId == orderIdTxt {
			valO = order
		}
	}

	if len(valO.OrderId) < 5 {
		fmt.Println("not found order, orderId:", orderId)
		return
	}
	var marketId uint32 = 15201
	var goodsId uint32 = 0

	if d, err := strconv.ParseUint(valO.MarketId, 10, 32); err == nil {
		marketId = uint32(d)
	}
	if d, err := strconv.ParseUint(valO.GoodsId, 10, 32); err == nil {
		goodsId = uint32(d)
	}

	t := time.Now().Unix()
	req := miProto.CancelOrderReq{
		Header: &miProto.MessageHead{
			FunCode:       proto.Uint32(FID_CancelOrderReq),
			RequestID:     proto.Uint32(GetSerialID()),
			AccountID:     proto.Uint64(r.accountId),
			AccessID:      proto.Uint32(0),
			ClientTime:    proto.Int64(t),
			GoodsID:       proto.Uint32(goodsId),
			UUID:          proto.String(r.GetUUID()),
			MarketID:      proto.Uint32(marketId),
			UserID:        proto.Uint32(r.userId),
			ResponseTopic: proto.String("order_rsp"),
			AccountID2:    proto.Uint64(0),
		},
		ClientSerialNo:  proto.String(strconv.FormatInt(t, 10)),
		ClientOrderTime: proto.String(time.Now().Format("2006-01-02 15:04:05")),
		ClientType:      proto.Uint32(2),
		OperateType:     proto.Uint32(1),
		OldOrderId:      proto.Uint64(orderId),
		AccountID:       proto.Uint64(r.accountId),
		OrderSrc:        proto.Uint32(1),
		OperatorID:      proto.Uint64(r.accountId),
		IP:              proto.String(getLocalIP()),
	}

	msg, _ := proto.Marshal(&req)
	r.send(FID_CancelOrderReq, GetSerialID(), GetSerialID(), msg, true, "cancelOrderReq")
}

func (r *Session) doRecv() {

	defer r.conn.Close()

	defer func() {
		r.bConnected = false
	}()

	for true {
		var err error
		var nRead int = 0
		headerBuf := make([]byte, 19)
		nRead, err = io.ReadFull(r.conn, headerBuf)
		if err != nil || nRead != 19 {
			fmt.Println(r.strAcc, ": read header data error:", err)
			return
		}

		//fmt.Println("read head:", headerBuf)

		p := &MiPacket{}
		err = p.UnPackHead(headerBuf)
		if err != nil {
			fmt.Println("parse header fail")
			return
		}

		if p.Length > 1024*1024*10 || p.Length < 24 {
			fmt.Println("packet too big or len error, len:", p.Length)
			return
		}

		dataBuf := make([]byte, p.Length-19)
		nRead, err = io.ReadFull(r.conn, dataBuf)
		if err != nil || nRead != len(dataBuf) {
			fmt.Println(r.strAcc, ": read data error:", err)
			return
		}

		if p.Length == 24 {
			//fmt.Println("heart packet.")
			continue
		}

		if Stcfg.PrintLog != 0 {
			log.Printf("%v recv funcode[%d] sid[%d] serial[%d] iLen:%d\n",
				r.strAcc, p.FunCode, p.SessionId, p.SerialNumber, p.Length)
		}

		if valReqLog != nil {
			valReqLog.AddRespond(p.SerialNumber)
		}

		go r.onMessage(p.SerialNumber, p.FunCode, p.Mode, dataBuf[:len(dataBuf)-5])
	}
}

func (r *Session) qrySomethingAfterLogin() {
	if Stcfg.QryFund == 1 {
		r.qryFund()
	}

	if Stcfg.QryExch == 1 {
		r.qryExchange()
	}

	if Stcfg.QryGoods == 1 {
		r.qryGoods()
	}

	if Stcfg.QryPosition == 1 {
		r.qryPosition()
	}

	if Stcfg.QryOrder == 1 {
		r.qryOrder()
	}

	if Stcfg.QryTrade == 1 {
		r.qryTrade()
	}

	if Stcfg.SubscribeQuote == 1 {
		// 订阅行情
		r.quote = new(QuoteSession)
		go r.quote.Do(r.loginId, r.token)
	}
}

func (r *Session) onChannelOrderRsp(serialNum uint32, msg []byte) {
	var d miProto.ChannelOrderRsp
	err := proto.Unmarshal(msg, &d)
	if err != nil {
		fmt.Println("unpack channelOrderRsp fail:", err)
		return
	}
	var orderId uint64 = 0
	if d.OrderID != nil {
		orderId = *d.OrderID
	}
	r.orderLog.addRsp(serialNum, orderId, *d.RetCode == 0)
	log.Printf("%v recv orderRsp, ret:%d desc:%v", r.strAcc, *d.RetCode, GetErrMsg(*d.RetCode))
}

func (r *Session) onCancelOrderRsp(msg []byte) {
	var d miProto.CancelOrderRsp
	if err := proto.Unmarshal(msg, &d); err != nil {
		fmt.Println("unpack cancelOrderRsp fail:", err)
		return
	}

	log.Printf("%v recv cancelOrderRsp, ret:%d desc:%v", r.strAcc, *d.RetCode, GetErrMsg(*d.RetCode))
}

func (r *Session) onLoginRsp(msg []byte) {
	var d miProto.LoginRsp
	err := proto.Unmarshal(msg, &d)
	if err != nil {
		fmt.Println("unpack loginrsp fail:", err)
		return
	}
	if len(d.AccountIDs) > 0 {
		r.accountId = d.AccountIDs[0]
		r.loginId = *d.LoginID
		r.userId = *d.UserID
		r.token = *d.Token
	}

	if Stcfg.PrintLog != 0 {
		log.Printf("%v recv loginId[%d] ret[%d]\n", r.strAcc, *d.LoginID, *d.RetCode)
	}

	if *d.RetCode == 0 {
		r.bLogin = true
		r.qrySomethingAfterLogin()
	}
}

func (r *Session) onLogoutRsp(msg []byte) {
	var p miProto.LogoutRsp
	err := proto.Unmarshal(msg, &p)
	if err != nil {
		fmt.Println("unpack logoutrsp fail:", err)
		return
	}

	r.bLogin = false

	if Stcfg.PrintLog != 0 {
		log.Printf("%v logout, ret:%d\n", r.loginId, *p.RetCode)
	}

	if r.quote != nil {
		r.quote.Stop()
	}
}

func (r *Session) onTradeNtf(msg []byte) {
	var d miProto.OrderDealedNtf
	if err := proto.Unmarshal(msg, &d); err != nil {
		fmt.Println("unpack orderDealNtf fail:", err)
		return
	}

	r.orderLog.addTradeNtf(*d.OrderID, *d.TradeQty)
}

// msg = body data
func (r *Session) onMessage(serialNum, funCode, mode uint32, msg []byte) {

	body, err := DecodeBody(mode, msg)
	if err != nil {
		fmt.Println("decode body fail:", err)
		return
	}

	if funCode == FID_LoginRsp {
		r.onLoginRsp(body)
	} else if funCode == FID_LogoutRsp {
		r.onLogoutRsp(body)
	} else if funCode == FID_ChannelOrderRsp {
		r.onChannelOrderRsp(serialNum, body)
	} else if funCode == FID_CancelOrderRsp {
		r.onCancelOrderRsp(body)
	} else if funCode == FID_OrderDealedNtf {
		r.onTradeNtf(body)
	} else {
		r.lck.Lock()
		f, ok := r.handleRsp[serialNum]
		r.lck.Unlock()

		if ok {
			f(funCode, body)
		}
	}
}
